//
//  server.c
//  LinuxPrinciplesandApplications
//
//  Created by SeanFelix‘sMacBook on 2023/12/4.
//

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <pthread.h>

#define BUFFER_SIZE 1024

pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; // 定义一个全局互斥锁，防止输入输出混乱

int maxLineLength = 50; // 假设最大行长度为80字符

// 线程函数，处理与客户端的通信。它接收来自客户端的消息，并允许服务器向客户端发送消息。
void *handle_client(void *client_sock) {
    int sockfd = *((int *)client_sock);
    char buffer[BUFFER_SIZE];
    ssize_t len;

    pthread_mutex_lock(&lock);
    printf("Enter message to send: "); // 首次显示输入提示
    fflush(stdout);
    pthread_mutex_unlock(&lock);

    while (1) {
        // 设置文件描述符集合
        fd_set read_fds;
        FD_ZERO(&read_fds);
        FD_SET(sockfd, &read_fds);
        FD_SET(STDIN_FILENO, &read_fds);

        // 使用 select 监听 sockfd 和 stdin
        if (select(sockfd + 1, &read_fds, NULL, NULL, NULL) < 0) {
            perror("select failed");
            break;
        }

        // 检查是否从 sockfd 接收到消息
        if (FD_ISSET(sockfd, &read_fds)) {
            memset(buffer, 0, BUFFER_SIZE);
            len = recv(sockfd, buffer, BUFFER_SIZE - 1, 0);
            if (len > 0) {
                buffer[len-1] = '\0';
                pthread_mutex_lock(&lock);
                printf("\rReceived message: %s", buffer);// 输出提示"\r"是返回行首的字符，它可以帮助重写当前行的内容。在这个情况下，它被用来清除"Enter message:"提示，以便打印接收到的消息。
                int messageLength = strlen(buffer);
                int spacesToFill = maxLineLength - messageLength - strlen("Received message: "); // 计算需要填充的空格数
                printf("%*s", spacesToFill, ""); // 使用足够的空格填充剩余部分
                printf("\n\rEnter message to send: ");// 重新输出，等待输入
                fflush(stdout);
                pthread_mutex_unlock(&lock);
            } else if (len == 0) {
                printf("\nClient disconnected\n");
                break;
            } else {
                perror("recv failed");
                break;
            }
        }

        // 检查是否有标准输入
        if (FD_ISSET(STDIN_FILENO, &read_fds)) {
            pthread_mutex_lock(&lock);
            fgets(buffer, BUFFER_SIZE, stdin);
            pthread_mutex_unlock(&lock);
            send(sockfd, buffer, strlen(buffer), 0);
            printf("Enter message to send: ");
            fflush(stdout);
        }
    }

    close(sockfd);
    return NULL;
}


int main(int argc, char *argv[]) {
    int sockfd, newsockfd, portno;
    struct sockaddr_in serv_addr, cli_addr;
    socklen_t clilen;
    pthread_t thread_id;

    if (argc < 2) {
        fprintf(stderr,"ERROR, no port provided\n");
        exit(1);
    }

    // 创建 TCP 套接字用于监听客户端连接
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0) {
        perror("ERROR opening socket");
        exit(1);
    }

    // 初始化套接字地址结构，绑定套接字到指定端口
    memset((char *) &serv_addr, 0, sizeof(serv_addr));
    portno = atoi(argv[1]);
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = INADDR_ANY;
    serv_addr.sin_port = htons(portno);

    if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
        perror("ERROR on binding");
        exit(1);
    }

    // 监听传入的客户端连接请求
    listen(sockfd, 5);
    clilen = sizeof(cli_addr);

    while (1) {
        // 接受客户端连接请求，为每个连接创建一个新的线程处理通信
        newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
        if (newsockfd < 0) {
            perror("ERROR on accept");
            continue;
        }

        // 使用 pthread_create 创建新线程，处理与客户端的通信
        if (pthread_create(&thread_id, NULL, handle_client, (void *)&newsockfd) < 0) {
            perror("could not create thread");
            close(newsockfd);
            continue;
        }

        // 分离线程，使得线程完成任务后能自行清理资源
        pthread_detach(thread_id);
    }

    close(sockfd);
    return 0;
}
